/* TBinderListConnectInspector.m
 * Written By:  Thomas Burkholder
 *
 * You may freely copy, distribute, and reuse the code in this example.
 * NeXT disclaims any warranty of any kind, expressed or  implied, as to its
 * fitness for any particular use.
 */


#import "TBinderListConnectInspector.h"
#import "TConnector.h"
#import "TDataSourceConnector.h"
#import "TBinderList.h"
#import "TBinder.h"
#import "TBinderInspector.h"
#import "SelPairAgent.h"
#import "SelectorBrowsingFunctions.h"
#import "../Utilities.subproj/SortedList.h"
#import "../Utilities.subproj/ConnectorAgent.h"
#import "../Utilities.subproj/ClassAdditions.h"
#import "../Utilities.subproj/SortedStorage.h"

static BOOL selecting;

@implementation TBinderListConnectInspector

id getDSConnectorForSource(id anObject)
{
	id sList = [[List alloc] init];
	id dsConnector;

	[[NXApp activeDocument] listConnectors:sList forSource:anObject
						filterClass:[TDataSourceConnector class]];
	dsConnector = [sList objectAt:0];
	[sList free];
	return dsConnector;
}

id getTConnectorForDestination(id anObject)
{
	id sList = [[List alloc] init];
	id tConnector;

	[[NXApp activeDocument] listConnectors:sList forDestination:anObject
						filterClass:[TConnector class]];
	tConnector = [sList objectAt:0];
	[sList free];
	return tConnector;
}

- init
{
	char buf[MAXPATHLEN + 1];
	id bundle;
	
	[super init];
	
	bundle = [NXBundle bundleForClass:[TBinderList class]];
	[bundle getPath:buf forResource:"TBinderListConnectInspector" ofType:"nib"];
	[NXApp loadNibFile:buf owner:self withNames:NO fromZone:[self zone]];
	[browser setDoubleAction:@selector(inspectBinderAfterDelay:)];
	[connectorList setAgent:[[ConnectorAgent alloc] init]];
	[accessorBrowser getTitleFromPreviousColumn:NO];
	[accessorBrowser setTitle:"DataSource" ofColumn:0];
	[accessorStorage setAgent:
					[[SelPairAgent alloc] initFrom:accessorStorage]];
	[browser setEmptySelectionEnabled:YES];
	[browser loadColumnZero];
	
	selecting = NO;
	
	return self;
}

- inspectorView
{
	return inspectorView;
}

- inspectBinderAfterDelay:sender
{
	[self perform:@selector(inspectBinder:) with:sender afterDelay:0
						cancelPrevious:NO];
	return self;
}

- inspectBinder:sender
{
	id isv = [inspectorView superview];
	id binderView = [binderInspector inspectorView];
	id conn=[connectorList objectAt:[[browser matrixInColumn:0]selectedRow]];

	[[isv window] disableFlushWindow];
	[isv replaceSubview:inspectorView with:binderView];
	[binderInspector inspectBinder:[conn binder] andReturnTo:self];
	[[binderView window] display];
	[[binderView window] reenableFlushWindow];
	[[binderView window] flushWindow];
	return self;
}

- selectAccessor:sender
{
	id conn;
	int col;

	// if there's no connection to apply it to, return.
	if (![[browser matrixInColumn:0] selectedCell])
		return self;

	// okay, we need to change the connection's binder:
	col = [accessorBrowser selectedColumn];
	conn = [connectorList objectAt:[[browser matrixInColumn:0] selectedRow]];
	if (0==col) {
		SEL s = sel_getUid([[[accessorBrowser matrixInColumn:0]
								selectedCell] stringValue]);
		if (s!=[[conn binder] dataSourceAccessor]) {
			[[conn binder] setDataSourceAccessor:s];
			[[conn binder] setDataSourceModifier:modifierFor(s)];

			// lets try not to lose the interfaceAccessor:
			[self showConnection:conn];
			if (![accessorBrowser selectedColumn]) { 
				[[conn binder] setInterfaceAccessor:(SEL)0];
				[[conn binder] setInterfaceModifier:(SEL)0];
			}
		}
	} else if (1==col) {
		SEL s = sel_getUid([[[accessorBrowser matrixInColumn:1]
								selectedCell] stringValue]);
		[[conn binder] setInterfaceAccessor:s];
		[[conn binder] setInterfaceModifier:modifierFor(s)];
	} else {
		[[conn binder] setDataSourceAccessor:(SEL)0];
		[[conn binder] setDataSourceModifier:(SEL)0];

		[[conn binder] setInterfaceAccessor:(SEL)0];
		[[conn binder] setInterfaceModifier:(SEL)0];
	}
	return self;
}

- selectDSConnection:sender
{
	id dsConnector = getDSConnectorForSource(object);
	selecting = YES;
	if ([sourceDisplayButton state]) {
		[NXApp displayConnectionBetween:[dsConnector source] and:
						[dsConnector destination]];
	} else {
		[NXApp stopConnecting];
	}
	return self;
}

- dataSourceOk:sender
{
	id dsConnector = getDSConnectorForSource(object);

	if ([sender tag]==0)  { // Want to connect a datasource
		dsConnector = [[TDataSourceConnector alloc] init];
		[dsConnector setSource:object];
		[dsConnector setDestination:[NXApp connectDestination]];
		[[NXApp activeDocument] addConnector:dsConnector];
		[object setDataSource:[dsConnector destination]];
		[[[dsConnector destination] class] methodSelectors:accessorStorage
								includeAncestors:YES
								filterWith:isAccessor];
		[accessorBrowser loadColumnZero];
		[sourceConnectButton setTitle:"Disconnect"];
		[sourceConnectButton setTag:1];
		[sourceDisplayButton setEnabled:YES];
		[sourceDisplayButton setTitle:[[dsConnector destination] name]];
	} else if ([sender tag]==1) { // Want to disconnect a datasource
		[[NXApp activeDocument] removeConnector:dsConnector];
		[dsConnector free];
		[accessorStorage empty];
		[accessorBrowser loadColumnZero];
		[sourceConnectButton setTitle:"Connect"];
		[sourceConnectButton setTag:0];
		[sourceDisplayButton setEnabled:NO];
		[sourceDisplayButton setTitle:""];
	}
	return self;
}

- showConnection:conn
{
	const char *dsa, *ia;
	char *str;
	
	// do the accessorBrowser
	dsa = sel_getName([[conn binder] dataSourceAccessor]);
	ia = sel_getName([[conn binder] interfaceAccessor]);
	str = malloc((dsa?strlen(dsa):0)+(ia?strlen(ia):0)+10);
	if (dsa && ia)
		sprintf(str,"/%s/%s",dsa,ia);
	else if (dsa)
		sprintf(str,"/%s",dsa);
	[accessorBrowser setPath:str];
	free(str);

	return self;
}

- selectConnection:sender
{
	id conn = [connectorList objectAt:[[browser matrixInColumn:0] selectedRow]];

	if (!conn) return self;

	selecting = YES;
	// draw the connection
	[NXApp displayConnectionBetween:[conn source] and:[conn destination]];
	[accessorBrowser loadColumnZero];
	[self showConnection:conn];

	[connectButton setTitle:"Disconnect"];
	[connectButton setTag:1];
	[connectButton setEnabled:YES];

	return self;
}

- constructBinder
{     //Assumes [NXApp isConnecting].
	id binder;

	binder = [[TBinder alloc] init];

	// we set up the datasource after reading all connections
	[binder setInterface:[NXApp connectDestination]];
	[binder setDataSource:nil];

	if ([[accessorBrowser matrixInColumn:0] selectedCell]) {
		[binder setDataSourceAccessor:
			sel_getUid([[[accessorBrowser matrixInColumn:0] selectedCell]
			stringValue])];
		[binder setDataSourceModifier:
			modifierFor([binder dataSourceAccessor])];
	}
	if ([[accessorBrowser matrixInColumn:1] selectedCell]) {
		[binder setInterfaceAccessor:
			sel_getUid([[[accessorBrowser matrixInColumn:1] selectedCell]
			stringValue])];
		[binder setInterfaceModifier:
			modifierFor([binder interfaceAccessor])];
	}
	return binder;
}

- ok:sender
{
	int i;
	id aConnector, b;

	if ([sender tag]==0) { // Want to connect a binder
		aConnector = [[TConnector alloc] init];
		[aConnector setBinder:[self constructBinder] andSource:object];
		[[NXApp activeDocument] addConnector:aConnector];
		[connectorList addObject:aConnector];
		[browser loadColumnZero];
		b = [browser matrixInColumn:0];
		[b selectCellAt:[b cellCount]-1 :0];
		[connectButton setTitle:"Disconnect"];
		[connectButton setTag:1];
	} else if ([sender tag] ==1) {  // Want to disconnect a binder
		i = [[browser matrixInColumn:0] selectedRow];
		aConnector = [connectorList removeObjectAt:i];
		
		[[NXApp activeDocument] removeConnector:aConnector];
		[aConnector free];
		[browser loadColumnZero];
		[[browser matrixInColumn:0] selectCellAt:-1 :-1];
		[connectButton setTitle:"Connect"];
		[connectButton setTag:0];
	}
	return [super ok:sender];
}

- revert:sender
{
	int s,cond;
	id dsConnector = getDSConnectorForSource(object);

	if (selecting) {
		selecting = NO;
		return self;
	}

	if (![inspectorView isDescendantOf:[window contentView]]) {
		id biv = [binderInspector inspectorView];
		[[biv superview] replaceSubview:biv with:inspectorView];
		[[inspectorView window] display];
	}
	if (dsConnector) {
		[sourceDisplayButton setEnabled:YES];
		[sourceDisplayButton setTitle:
						[[dsConnector destination] name]];
		cond = ([NXApp isConnecting] &&
				[dsConnector destination]==[NXApp connectDestination]);
		[sourceDisplayButton setState:cond];
		[sourceConnectButton setTitle:"Disconnect"];
		[sourceConnectButton setTag:1];
		[[[dsConnector destination] class] methodSelectors:accessorStorage
								includeAncestors:YES
								filterWith:isAccessor];
	} else {
		[sourceDisplayButton setEnabled:NO];
		[sourceDisplayButton setTitle:""];
		[sourceConnectButton setTitle:"Connect"];
		[sourceConnectButton setTag:0];
	}
	[sourceConnectButton setEnabled:[NXApp isConnecting]];

	[[NXApp activeDocument] listConnectors:connectorList forSource:object
							filterClass:[TConnector class]];
	[browser loadColumnZero];
	[accessorBrowser loadColumnZero];

	if ([NXApp isConnecting]) {
		s = [connectorList indexOf:
			getTConnectorForDestination([NXApp connectDestination])];
		if (s == NX_NOT_IN_LIST) {
			[[browser matrixInColumn:0] selectCellAt:-1 :-1];
			[connectButton setTitle:"Connect"];
			[connectButton setTag:0];
			[connectButton setEnabled:YES];
		} else {
			[accessorBrowser loadColumnZero];
			[self showConnection:[connectorList objectAt:s]];
			[[browser matrixInColumn:0] selectCellAt:s :0];
			[connectButton setTitle:"Disconnect"];
			[connectButton setTag:1];
			[connectButton setEnabled:YES];
		}
	} else {
		[[browser matrixInColumn:0] selectCellAt:-1 :-1];
		[connectButton setTitle:"No Selection"];
		[connectButton setEnabled:NO];
		[connectButton setTag:2];
	}

	return [super revert:sender];
}

- (BOOL)wantsButtons
{
	return NO;
}

@end

@implementation TBinderList (ConnectionsInspector)

-(const char *)getConnectInspectorClassName
{
	NXEvent *ev = [NXApp currentEvent];

	// NOTE:  This doesn't work on PC's and most NextStations, due
	// to the fact that control-alt is mapped to be a Help key!!!
	// expect this to go away later with the more complete version of
	// superclass-inspector access.
	if (ev->flags & NX_ALTERNATEMASK)
		return [super getConnectInspectorClassName];
	else
		return "TBinderListConnectInspector";
}

-(const char *)getEditorClassName
{
	NXEvent *ev = [NXApp currentEvent];

	// lets try not to confuse people... unless they really want
	// to be confused!!!
	if (ev->flags & NX_ALTERNATEMASK)
		return [super getConnectInspectorClassName];
	else
		return NULL;
}

- (NXImage *)getIBImage
{
	return [NXImage findImageNamed:"TBinderListIcon"];
}

@end

